package
fr.n7.sma.model;

import java.util.HashMap;
import java.util.Map;

import javax.swing.event.EventListenerList;

import fr.n7.sma.model.listeners.CellListener;
import fr.n7.sma.model.listeners.events.CellEvent;

public class Cell<AdditionnalProperty> implements ICell<AdditionnalProperty>, CellListener<AdditionnalProperty> {

	private AdditionnalProperty additionnalProperty;
	
	private EventListenerList listeners;
	
	private Map<String, Short> resources;
	private Map<String, Short> neighboursResources;
	
	//private ArrayList<AttractorEffect> attractorEffects;
	private float attractorEffects;
	private float pheroEffects;
	
	private short[] pos;

	public Cell(short[] p) {
		this(new HashMap<String, Short>(), p);
	}
	
	public Cell(
			Map<String, Short> resources,
			short[] p) {
		this.resources = resources;
		this.pos = p;
		this.listeners = new EventListenerList();
		this.neighboursResources = new HashMap<String, Short>();
		this.attractorEffects =0;
		this.pheroEffects = 0;
	}

	
	public void addCellListener(CellListener<AdditionnalProperty> l){
		listeners.add(CellListener.class, l);
		for(String name : resources.keySet()){
			l.cellChanged(new CellEvent<AdditionnalProperty>(this, name, resources.get(name)));
		}
	}

	
	public void removeCellListener(CellListener<AdditionnalProperty> l){
		listeners.remove(CellListener.class, l);
	}

	/**
	 * @param name the name of the resource
	 * @return the new number of resources
	 */
	public Short addOneResource(String name){
		return addResource((short)(1), name);
	}
	
	/**
	 * @param name the name of the resource
	 * @return the new number of resources
	 */
	public Short removeOneResource(String name){
		return addResource((short)(-1), name);
	}
	
	/**
	 * @param number the number of resources to remove
	 * @param name the name of the resource
	 * @return the new number of resources
	 */
	public Short removeResource(short number, String name){
		return addResource((short)(-number), name);
	}
	
	/**
	 * @param number the number of resources to add
	 * @param name the name of the resource
	 * @return the new number of resources
	 */
	synchronized public Short addResource(short number, String name){
		Short lastNumber = resources.get(name);
		Short newNumber = number;
		if(lastNumber != null){
			newNumber = (short)(newNumber + lastNumber);
		}
		if(newNumber < 0){
			throw new Error("Impossible d'enlever une resource non présente.."+ newNumber);
		}
		resources.put(name, newNumber);
		resourcesQuantityModifiedNotification(name, number);
		return number;
	}
	
	/**
	 * @param number the number of resources to remove
	 * @param name the name of the resource
	 * @return the new number of resources
	 */
	public Short deleteResource(short number, String name){
		return addNewResource((short)(-number), name);
	}
	
	/**
	 * @param number the number of resources to add
	 * @param name the name of the resource
	 * @return the new number of resources
	 */
	synchronized public Short addNewResource(short number, String name){
		Short lastNumber = resources.get(name);
		short newNumber = number;
		if(lastNumber != null){
			newNumber += lastNumber;
		}
		if(newNumber < 0){
			throw new Error("Impossible d'enlever une resource non présente..");
		}
		resources.put(name, newNumber);
		
		resourcesQuantityModifiedNotification(name, number);
		
		return number;
	}
	
	private void resourcesQuantityModifiedNotification(String name, short number){
		for(CellListener<AdditionnalProperty> cl : listeners.getListeners(CellListener.class)){
			cl.cellChanged(new CellEvent<AdditionnalProperty>(this, name, number));
		}
	}
	
	/**
	 * @return the cell position
	 */
	public short[] getPosition() {
		return pos;
	}

	public Map<String, Short> getNeighboursResources() {
		return neighboursResources;
	}
	
	public Map<String, Short> getResources() {
		return resources;
	}

	public boolean isFree() {
		int max = 0;
		for(Short nb : resources.values()){
			max = Math.max(nb, max);
		}
		return max == 0;
	}

	public AdditionnalProperty getAdditionnalProperty() {
		return additionnalProperty;
	}

	public void setAdditionnalProperty(AdditionnalProperty additionnalProperty) {
		this.additionnalProperty = additionnalProperty;
	}
	
	public void addAttractorEffect(int x, int y, Attractor<AdditionnalProperty> a) {
		attractorEffects += AttractorEffect.compute(a, x, y);
		notifyCellChanged(new CellEvent<AdditionnalProperty>(this, CellEvent.EventType.ATTRACTOR_CHANGED));
	}
	
	public void removeAttractorEffect(int x, int y, Attractor<AdditionnalProperty> a) {
		attractorEffects -= AttractorEffect.compute(a, x, y);
		notifyCellChanged(new CellEvent<AdditionnalProperty>(this, CellEvent.EventType.ATTRACTOR_CHANGED));
	}
	
	public void clearAttractorEffect(boolean attractor, boolean phero) {
		if(attractor) attractorEffects = 0;
		if(phero) pheroEffects = 0;
		notifyCellChanged(new CellEvent<AdditionnalProperty>(this, CellEvent.EventType.ATTRACTOR_CHANGED));
	}

	public float getAttractorEffects() {
		return attractorEffects;
	}
	
	public void addPheroEffect(int x, int y, Attractor<AdditionnalProperty> a) {
		pheroEffects += AttractorEffect.compute(a, x, y);
		notifyCellChanged(new CellEvent<AdditionnalProperty>(this, CellEvent.EventType.ATTRACTOR_CHANGED));
	}
	
	public float getPheroEffects() {
		return pheroEffects;
	}
	
	public void notifyCellChanged(CellEvent<AdditionnalProperty> ce){
		for(CellListener<AdditionnalProperty> cl : listeners.getListeners(CellListener.class)){
			cl.cellChanged(ce);
		}
	}
	
	public void cellChanged(CellEvent<AdditionnalProperty> ce) {
		switch(ce.getEventType()){
		case RESOURCE_NUMBER_CHANGED :
			String name = ce.getResourceName();
			Short nb = neighboursResources.get(name);
			neighboursResources.put(name, (short)(((nb == null) ? 0 : nb) + ce.getResourceNumberModification()));
			break;
		}
	}

	public void evaporate(float ratio) {
		if(Float.compare(this.pheroEffects, 0) != 0){
			this.pheroEffects *= ratio;
			notifyCellChanged(new CellEvent<AdditionnalProperty>(this, CellEvent.EventType.ATTRACTOR_CHANGED));
		}
	}

}
